package org.xqh.study.leetcode.mulitthread;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.function.IntConsumer;

/**
 * @ClassName FizzBuzzCyclicBarrier
 * @Description TODO
 * @Author xuqianghui
 * @Date 2020/12/26 16:13
 * @Version 1.0
 */
public class FizzBuzzCyclicBarrier {

    static class FizzBuzz {

        private int n;
        private int c;

        CyclicBarrier cyclic = new CyclicBarrier(1);

        public FizzBuzz(int n) {
            this.n = n;
            this.c = 1;
        }

        // printFizz.run() outputs "fizz".
        public void fizz(Runnable printFizz) throws InterruptedException, BrokenBarrierException {

            for (; ; ) {
                if (c > n) {
                    break;
                }
                if (c % 3 == 0 && c % 5 != 0) {
//                    printFizz.run();
                    System.out.println("fizz");
                    c++;
                    cyclic.await();
                }
            }
        }

        // printBuzz.run() outputs "buzz".
        public void buzz(Runnable printBuzz) throws InterruptedException, BrokenBarrierException {
            for (; ; ) {
                if (c > n) {
                    break;
                }
                if (c % 5 == 0 && c % 3 != 0) {
//                    printBuzz.run();
                    System.out.println("buzz");
                    c++;
                    cyclic.await();
                }
            }

        }

        // printFizzBuzz.run() outputs "fizzbuzz".
        public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException, BrokenBarrierException {
            for (; ; ) {
                if (c > n) {
                    break;
                }
                if (c % 3 == 0 && c % 5 == 0) {
//                    printFizzBuzz.run();
                    System.out.println("fizzbuzz");
                    c++;
                    cyclic.await();
                }
            }
        }

        // printNumber.accept(x) outputs "x", where x is an integer.
        public void number(IntConsumer intConsumer) throws InterruptedException, BrokenBarrierException {
            for (;;) {
                if (c > n) {
                    break;
                }
                if (c % 3 != 0 && c % 5 != 0) {
//                    intConsumer.accept(c);
                    System.out.println(c);
                    c++;
                    cyclic.await();
                }
            }
        }
    }

    public static void main(String[] args) {
        FizzBuzz fb = new FizzBuzz(15);
        new Thread(new Thread1(fb)).start();
        new Thread(new Thread2(fb)).start();
        new Thread(new Thread3(fb)).start();
        new Thread(new Thread4(fb)).start();
    }

    public static class Thread1 implements Runnable{
        private FizzBuzz fb;
        public Thread1(FizzBuzz fb){
            this.fb = fb;
        }

        @Override
        public void run() {
            try {
                fb.fizz(this);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static class Thread2 implements Runnable{
        private FizzBuzz fb;
        public Thread2(FizzBuzz fb){
            this.fb = fb;
        }

        @Override
        public void run() {
            try {
                fb.buzz(this);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static class Thread3 implements Runnable{
        private FizzBuzz fb;
        public Thread3(FizzBuzz fb){
            this.fb = fb;
        }

        @Override
        public void run() {
            try {
                fb.fizzbuzz(this);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static class Thread4 implements Runnable{
        private FizzBuzz fb;
        public Thread4(FizzBuzz fb){
            this.fb = fb;
        }

        @Override
        public void run() {
            try {
                fb.number(new IntConsumer() {
                    @Override
                    public void accept(int value) {

                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
